package com.lsl.rdf.service.impl;

import com.alibaba.fastjson.JSON;
import com.lsl.rdf.UnauthorizedException;
import com.lsl.rdf.UnexpectedException;
import com.lsl.rdf.config.UserProperties;
import com.lsl.rdf.constant.TokenResult;
import com.lsl.rdf.constant.enums.CommonConstants;
import com.lsl.rdf.distributedLock.DistributedLock;
import com.lsl.rdf.service.UserSupport;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * Created by lsl on 2021/4/15.
 */
@Slf4j
@Service
public class UserSupportImpl implements UserSupport {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private DistributedLock distributedLock;
    @Autowired
    private UserProperties userProperties;
    @Autowired
    private HttpServletRequest request;

    @Override
    public TokenResult createToken(Long userId) {
        if (Objects.isNull(userId)) {
            log.error("UserSupportImpl.createToken 获取token失败！userId为空");
            throw new UnexpectedException("用户数据异常");
        }
        String lockKey = CommonConstants.getUserTokenLockKey(userId);
        boolean lock = distributedLock.acquireLock(lockKey, 5, 20);
        if (!lock) {
            log.error("UserSupportImpl.createToken 生成token失败，获取锁失败！userId: {}", userId);
            throw new UnexpectedException("系统繁忙，请稍后重试");
        }
        // 删除旧token
        removeOldUserToken(userId);

        String accessToken = UUID.randomUUID().toString();
        String refreshToken = UUID.randomUUID().toString();

        String accessTokenKey = CommonConstants.getAccessTokenKey(accessToken);
        String refreshTokenKey = CommonConstants.getRefreshTokenKey(refreshToken);
        String userTokenKey = CommonConstants.getUserTokenKey(userId);

        TokenResult result = new TokenResult();
        result.setAccessToken(accessToken);
        result.setExpireIn(userProperties.getAccessTokenExpires());
        result.setRefreshToken(refreshToken);

        redisTemplate.opsForValue().set(userTokenKey, JSON.toJSONString(result));
        redisTemplate.opsForValue().set(refreshTokenKey, String.valueOf(userId), userProperties.getRefreshTokenExpires(), TimeUnit.SECONDS);
        redisTemplate.opsForValue().set(accessTokenKey, String.valueOf(userId), userProperties.getAccessTokenExpires(), TimeUnit.SECONDS);

        distributedLock.releaseLock(lockKey);
        return result;
    }

    @Override
    public TokenResult refreshToken() {
        String refreshToken = getHeaderToken(CommonConstants.REFRESH_TOKEN_HEADER_ATTR);
        String userId = redisTemplate.opsForValue().get(CommonConstants.getRefreshTokenKey(refreshToken));
        checkUnauthorized(StringUtils.isBlank(userId), "UserSupportImpl.refreshToken 刷新token失败，无效token！refreshToken: " + refreshToken);
        return createToken(Long.valueOf(Objects.requireNonNull(userId)));
    }

    /**
     * 获取access token 或 refresh token
     *
     * @param headerName 获取header值的header名称
     * @return token
     */
    private String getHeaderToken(String headerName) {
        if (Objects.isNull(request)) {
            log.error("UserSupportImpl.getHeaderToken 获取{}失败！request 为空", headerName);
            throw new UnexpectedException("获取token失败");
        }
        String token = request.getHeader(headerName);
        checkUnauthorized((StringUtils.isBlank(token) || token.length() != 36),
                "UserSupportImpl.getHeaderToken 获取 " + headerName + "，无效token");
        return token;
    }

    @Override
    public Long getToken(String accessToken) {
        checkUnauthorized(StringUtils.isBlank(accessToken), "UserSupportImpl.getToken 获取token内容失败，accessToken为空");

        String accessTokenKey = CommonConstants.getAccessTokenKey(accessToken);
        String userId = redisTemplate.opsForValue().get(accessTokenKey);

        checkUnauthorized(StringUtils.isBlank(userId), "UserSupportImpl.getToken 获取token内容失败，无效token，accessToken: " + accessToken);
        return Long.valueOf(Objects.requireNonNull(userId));
    }

    @Override
    public Boolean removeToken() {
        String accessToken = getHeaderToken(CommonConstants.ACCESS_TOKEN_HEADER_ATTR);
        String userId = redisTemplate.opsForValue().get(CommonConstants.getAccessTokenKey(accessToken));
        checkUnauthorized(StringUtils.isBlank(userId), "UserSupportImpl.removeToken 移除token失败，无效token，accessToken: " + accessToken);
        return removeOldUserToken(Long.valueOf(Objects.requireNonNull(userId)));
    }

    private void checkUnauthorized(boolean isThrow, String logMsg) {
        if (isThrow) {
            log.error(logMsg);
            throw new UnauthorizedException("无效token");
        }
    }

    /**
     * 删除用户token
     *
     * @param userId 用户id
     */
    private Boolean removeOldUserToken(Long userId) {
        if (Objects.isNull(userId)) {
            log.error("UserSupportImpl.removeOldUserToken 删除用户token信息失败！userId为空");
            return Boolean.FALSE;
        }
        String userJson = redisTemplate.opsForValue().get(CommonConstants.getUserTokenKey(userId));
        if (StringUtils.isNotBlank(userJson)) {
            TokenResult oldToken = JSON.parseObject(userJson, TokenResult.class);
            if (Objects.nonNull(oldToken)) {
                redisTemplate.delete(CommonConstants.getAccessTokenKey(oldToken.getAccessToken()));
                redisTemplate.delete(CommonConstants.getRefreshTokenKey(oldToken.getRefreshToken()));
            }
        }
        return Boolean.TRUE;
    }
}
